#pragma once

#include "mod.h"

template <class T>
[[sfc_inline]] void* operator new(sfc::usize, T* p) {
  return p;
}

namespace sfc::mem {

template <class T>
union MaybeUninit {
  using T0 = T;
  using T1 = Nil;

  T0 _0;
  T1 _1 = {};
  [[sfc_inline]] ~MaybeUninit() {}
};

template <class T>
[[sfc_inline]] void swap(T& x, T& y) {
  auto z = static_cast<T&&>(x);
  new (&x) T{static_cast<T&&>(y)};
  new (&y) T{static_cast<T&&>(z)};
}

template <class T, class... U>
[[sfc_inline]] auto replace(T& dst, U&&... src) -> T {
  auto tmp = static_cast<T&&>(dst);
  new (&dst) T{static_cast<T&&>(src)...};
  return tmp;
}

template <class T>
[[sfc_inline]] auto move(T& ref) -> T&& {
  return static_cast<T&&>(ref);
}

template <class T>
[[sfc_inline]] auto take(T& dst) -> T {
  auto tmp = static_cast<T&&>(dst);
  new (&dst) T{};
  return tmp;
}

template <class T>
[[sfc_inline]] void drop(T& x) {
  x.~T();
}

template <class T>
[[sfc_inline]] void forget(T& x) {
  alignas(T) u8 buf[sizeof(T)];
  new (buf) T(static_cast<T&&>(x));
}

}  // namespace sfc::mem
